<template>
  <div
    ref="wftCustomDraggableRef"
    :style="style"
    :class="[{
      [classNameActive]: enabled,
      [classNameDragging]: dragging,
      [classNameResizing]: resizing,
      [classNameDraggable]: draggable,
      [classNameResizable]: resizable
    }, className]"
    @mousedown.stop="elementMouseDown"
    @touchstart.stop="elementTouchDown"
    @contextmenu.stop="onContextMenu"
  >
    <div
      v-for="handle in actualHandles"
      :key="handle"
      :class="[classNameHandle, classNameHandle + '-' + handle]"
      :style="handleStyle(handle)"
      @mousedown.stop.prevent="handleDown(handle, $event)"
      @touchstart.stop.prevent="handleTouchDown(handle, $event)"
    >
      <slot :name="handle"></slot>
    </div>
    <slot></slot>
  </div>
</template>
<script setup lang='ts'>
import { matchesSelectorToParentElements, getComputedSize, addEvent, removeEvent } from './utils/dom'
import { computeWidth, computeHeight, restrictToBounds, snapToGrid } from './utils/fns'
import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue'

const wftCustomDraggableRef = ref<HTMLDivElement>()

const events = {
  mouse: {
    start: 'mousedown',
    move: 'mousemove',
    stop: 'mouseup'
  },
  touch: {
    start: 'touchstart',
    move: 'touchmove',
    stop: 'touchend'
  }
}
// 禁止用户选取
const userSelectNone: { [k: string]: any } = {
  userSelect: 'none',
  MozUserSelect: 'none',
  WebkitUserSelect: 'none',
  MsUserSelect: 'none'
}
// 用户选中自动
const userSelectAuto: { [k: string]: any } = {
  userSelect: 'auto',
  MozUserSelect: 'auto',
  WebkitUserSelect: 'auto',
  MsUserSelect: 'auto'
}

let eventsFor = events.mouse

const props = withDefaults(defineProps<{
  className?: string,
  classNameDraggable?: string,
  classNameResizable?: string,
  classNameDragging?: string,
  classNameResizing?: string,
  classNameActive?: string,
  classNameHandle?: string,
  disableUserSelect?: boolean,
  enableNativeDrag?: boolean,
  preventDeactivation?: boolean,
  active?: boolean,
  draggable?: boolean,
  resizable?: boolean,
  lockAspectRatio?: boolean,
  w?: number | string,
  h?: number | string,
  minWidth?: number,
  minHeight?: number,
  maxWidth?: number | null,
  maxHeight?: number | null,
  x?: number,
  y?: number,
  z?: string | number,
  handles?: string[],
  dragHandle?: string | null,
  dragCancel?: string | null,
  axis?: string,
  grid?: number[],
  parent?: boolean | string,
  onDragStart?: (...args: any[]) => any,
  onDrag?: (...args: any[]) => any,
  onResizeStart?: (...args: any[]) => any,
  onResize?: (...args: any[]) => any,
  isConflictCheck?: boolean,
  snap?: boolean,
  snapTolerance?: number,
  scaleRatio?: number,
  handleInfo?: { [key: string]: any },
  panelContainer?: string | null,
  excludeContainer?: string | null
}>(), {
  className: 'vdr',
  classNameDraggable: 'draggable',
  classNameResizable: 'resizable',
  classNameDragging: 'dragging',
  classNameResizing: 'resizing',
  classNameActive: 'active',
  classNameHandle: 'handle',
  disableUserSelect: true,
  enableNativeDrag: false,
  preventDeactivation: false,
  active: false,
  draggable: true,
  resizable: true,
  lockAspectRatio: false,
  w: 200,
  h: 200,
  minWidth: 0,
  minHeight: 0,
  maxWidth: null,
  maxHeight: null,
  x: 0,
  y: 0,
  z: 'auto',
  handles: () => ['tl', 'tm', 'tr', 'mr', 'br', 'bm', 'bl', 'ml'],
  dragHandle: null,
  dragCancel: null,
  axis: 'both',
  grid: () => [1, 1],
  parent: false,
  onDragStart: () => true,
  onDrag: () => true,
  onResizeStart: () => true,
  onResize: () => true,
  isConflictCheck: false,
  snap: false,
  snapTolerance: 5,
  scaleRatio: 1,
  handleInfo: () => ({size: 8, offset: -5, switch: true }),
  panelContainer: null,
  excludeContainer: null
})

const left = ref(props.x)
const top = ref(props.y)
const right = ref(0)
const bottom = ref(0)
const width = ref(0)
const height = ref(0)
const widthTouched = ref(false)
const heightTouched = ref(false)
const aspectFactor = ref<any>(null)
const parentWidth = ref(0)
const parentHeight = ref(0)
const minW = ref(props.minWidth)
const minH = ref(props.minHeight)
const maxW = ref(props.maxWidth)
const maxH = ref(props.maxHeight)
const handle = ref<string | null>(null)
const enabled = ref(props.active)
const resizing = ref(false)
const dragging = ref(false)
const zIndex = ref(props.z)
const bounds = ref<any>(null)
const mouseClickPosition = ref<any>(null)

const handleStyle = computed(() => {
  return (stick: string) => {
    if (!props.handleInfo.switch) return { display: enabled.value ? 'block' : 'none' }
    const size = Number((props.handleInfo.size / props.scaleRatio).toFixed(2))
    const offset = (props.handleInfo.offset / props.scaleRatio).toFixed(2)
    const center = (size / 2).toFixed(2)
    const styleMap: { [k: string]: any } = {
      tl: {
        top: `${offset}px`,
        left: `${offset}px`
      },
      tm: {
        top: `${offset}px`,
        left: `calc(50% - ${center}px)`
      },
      tr: {
        top: `${offset}px`,
        right: `${offset}px`
      },
      mr: {
        top: `calc(50% - ${center}px)`,
        right: `${offset}px`
      },
      br: {
        bottom: `${offset}px`,
        right: `${offset}px`
      },
      bm: {
        bottom: `${offset}px`,
        right: `calc(50% - ${center}px)`
      },
      bl: {
        bottom: `${offset}px`,
        left: `${offset}px`
      },
      ml: {
        top: `calc(50% - ${center}px)`,
        left: `${offset}px`
      }
    }
    const stickStyle: { [k: string]: any } = {
      width: `${size}px`,
      height: `${size}px`,
      top: styleMap[stick].top,
      left: styleMap[stick].left,
      right: styleMap[stick].right,
      bottom: styleMap[stick].bottom
    }
    stickStyle.display = enabled.value ? 'block' : 'none'
    return stickStyle
  }
})
const computedWidth = computed(() => {
  if (props.w === 'auto') {
    if (!widthTouched.value) {
      return 'auto'
    }
  }
  return width.value + 'px'
})
const computedHeight = computed(() => {
  if (props.h === 'auto') {
    if (!heightTouched.value) {
      return 'auto'
    }
  }
  return height.value + 'px'
})
const style = computed(() => {
  return {
    transform: `translate(${left.value}px, ${top.value}px)`,
    width: computedWidth.value,
    height: computedHeight.value,
    zIndex: zIndex.value,
    ...(dragging.value && props.disableUserSelect ? userSelectNone : userSelectAuto)
  }
})
const actualHandles = computed(() => {
  if (!props.resizable) return []
  return props.handles
})
const resizingOnX = computed(() => {
  if(!handle.value) return false
  return (Boolean(handle.value) && (handle.value.includes('l') || handle.value.includes('r')))
})
const resizingOnY = computed(() => {
  if(!handle.value) return false
  return (Boolean(handle.value) && (handle.value.includes('t') || handle.value.includes('b')))
})

const emits = defineEmits<{
  (e: 'activated', ...params: any[]): void;
  (e: 'deactivated', ...params: any[]): void;
  (e: 'update:active', ...params: any[]): void;
  (e: 'resizing', ...params: any[]): void;
  (e: 'refLineParams', ...params: any[]): void;
  (e: 'dragstop', ...params: any[]): void;
  (e: 'resizestop', ...params: any[]): void;
  (e: 'dragging', ...params: any[]): void;
  (e: 'contextmenu', ...params: any[]): void;
}>()

watch(() => props.active, val => {
  enabled.value = val
  if (val) {
    emits('activated')
  } else {
    emits('deactivated')
  }
})
watch(() => props.z, val => {
  if ((typeof val == 'number' && val >= 0) || val === 'auto') {
    zIndex.value = val
  }
})
watch(() => props.x, val => {
  if (resizing.value || dragging.value) {
    return
  }
  if (props.parent) {
    bounds.value = calcDragLimits()
  }
  moveHorizontally(val)
})
watch(() => props.y, val => {
  if (resizing.value || dragging.value) {
    return
  }
  if (props.parent) {
    bounds.value = calcDragLimits()
  }
  moveVertically(val)
})
watch(() => props.lockAspectRatio, val => {
  if (val) {
    aspectFactor.value = width.value / height.value
  } else {
    aspectFactor.value = undefined
  }
})
watch(() => props.minWidth, val => {
  if (val > 0 && val <= width.value) {
    minW.value = val
  }
})
watch(() => props.minHeight, val => {
  if (val > 0 && val <= height.value) {
    minH.value = val
  }
})
watch(() => props.maxWidth, val => {
  maxW.value = val
})
watch(() => props.maxHeight, val => {
  maxH.value = val
})
watch(() => props.w, val => {
  if (resizing.value || dragging.value) {
    return
  }
  if(props.parent) {
    bounds.value = calcResizeLimits()
  }
  changeWidth((val as number))
})
watch(() => props.h, val => {
  if (resizing.value || dragging.value) {
    return
  }
  if(props.parent) {
    bounds.value = calcResizeLimits()
  }
  changeHeight((val as number))
})
watch(() => props.isConflictCheck, () => {
  settingAttribute()
})
watch(() => props.enableNativeDrag, () => {
  wftCustomDraggableRef.value!.ondragstart = () => props.enableNativeDrag
})

// eslint-disable-next-line 无效的prop：minWidth不能大于maxWidth
if (props.maxWidth && props.minWidth > props.maxWidth) console.warn('[Vdr warn]: Invalid prop: minWidth cannot be greater than maxWidth')
// eslint-disable-next-line 无效prop：minHeight不能大于maxHeight'
if (props.maxHeight && props.minHeight > props.maxHeight) console.warn('[Vdr warn]: Invalid prop: minHeight cannot be greater than maxHeight')
resetBoundsAndMouseState()

onMounted(() => {
  if (!props.enableNativeDrag) {
    wftCustomDraggableRef.value!.ondragstart = () => false
  }
  const [_parentWidth, _parentHeight] = getParentSize()
  parentWidth.value = _parentWidth!
  parentHeight.value = _parentHeight!
  const [_width, _height] = getComputedSize((wftCustomDraggableRef.value as HTMLDivElement))
  aspectFactor.value = (props.w !== 'auto' ? props.w : _width) / (props.h !== 'auto' ? props.h : _width)
  width.value = props.w !== 'auto' ? props.w : _width
  height.value = props.h !== 'auto' ? props.h : _height
  right.value = parentWidth.value - width.value - left.value
  bottom.value = parentHeight.value - height.value - top.value
  settingAttribute()
  // 优化：取消选中的行为优先绑定在父节点上
  // const parentElement = this.$el.parentNode
  addEvent(document.documentElement, 'mousedown', deselect)
  addEvent(document.documentElement, 'touchend touchcancel', deselect)
  addEvent(window, 'resize', checkParentSize)
})
onBeforeUnmount(() => {
  removeEvent(document.documentElement, 'mousedown', deselect)
  removeEvent(document.documentElement, 'touchstart', handleUp)
  removeEvent(document.documentElement, 'mousemove', move)
  removeEvent(document.documentElement, 'touchmove', move)
  removeEvent(document.documentElement, 'mouseup', handleUp)
  removeEvent(document.documentElement, 'touchend touchcancel', deselect)
  removeEvent(window, 'resize', checkParentSize)
})

// 右键菜单
function onContextMenu (e: any) {
  emits('contextmenu', e)
}

// 元素触摸按下
function elementTouchDown (e: any) {
  eventsFor = events.touch

  elementDown(e)
}
function elementMouseDown (e: any) {
  eventsFor = events.mouse
  elementDown(e)
}

// 元素按下
function elementDown (e: any) {
  if (e instanceof MouseEvent && e.which !== 1) {
    return
  }
  const target = e.target || e.srcElement
  if (wftCustomDraggableRef.value!.contains(target)) {
    if (props.onDragStart(e) === false) {
      return
    }
    if (
      (props.dragHandle && !matchesSelectorToParentElements(target, props.dragHandle, wftCustomDraggableRef.value)) ||
      (props.dragCancel && matchesSelectorToParentElements(target, props.dragCancel, wftCustomDraggableRef.value))
    ) {
      dragging.value = false
      return
    }
    if (!enabled.value) {
      enabled.value = true
      emits('activated')
      emits('update:active', true)
    }
    if (props.draggable) {
      dragging.value = true
    }
    mouseClickPosition.value.mouseX = e.touches ? e.touches[0].pageX : e.pageX
    mouseClickPosition.value.mouseY = e.touches ? e.touches[0].pageY : e.pageY
    mouseClickPosition.value.left = left.value
    mouseClickPosition.value.right = right.value
    mouseClickPosition.value.top = top.value
    mouseClickPosition.value.bottom = bottom.value
    mouseClickPosition.value.w = width.value
    mouseClickPosition.value.h = height.value
    if (props.parent) {
      bounds.value = calcDragLimits()
    }
    addEvent(document.documentElement, eventsFor.move, move)
    addEvent(document.documentElement, eventsFor.stop, handleUp)
  }
}


// 控制柄按下
function handleDown(handle: any, e:any) {
  if(e instanceof MouseEvent && e.which !== 1) {
    return
  }
  if(props.onResizeStart(handle, e) === false) {
    return
  }
  if(e.stopPropagation) e.stopPropagation()
  // Here we avoid a dangerous recursion by faking
  // corner handles as middle handles
  if(props.lockAspectRatio && !handle.includes('m')) {
    handle.value = 'm' + handle.substring(1)
  } else {
    handle.value = handle
  }
  resizing.value = true
  mouseClickPosition.value.mouseX = e.touches ? e.touches[0].pageX : e.pageX
  mouseClickPosition.value.mouseY = e.touches ? e.touches[0].pageY : e.pageY
  mouseClickPosition.value.left = left.value
  mouseClickPosition.value.right = right.value
  mouseClickPosition.value.top = top.value
  mouseClickPosition.value.bottom = bottom.value
  mouseClickPosition.value.w = width.value
  mouseClickPosition.value.h = height.value
  bounds.value = calcResizeLimits()
  addEvent(document.documentElement, eventsFor.move, handleResize)
  addEvent(document.documentElement, eventsFor.stop, handleUp)
}

// 控制柄触摸按下
function handleTouchDown(handle: any, e: any) {
  eventsFor = events.touch
  handleDown(handle, e)
}

// 移动
function move (e: any) {
  if (resizing.value) {
    handleResize(e)
  } else if (dragging.value) {
    handleDrag(e)
  }
}
// 元素移动
async function handleDrag(e: any) {
  const axis = props.axis
  const grid = props.grid
  const _bounds = bounds.value
  const _mouseClickPosition = mouseClickPosition.value
  const tmpDeltaX = axis && axis !== 'y' ? _mouseClickPosition.mouseX - (e.touches ? e.touches[0].pageX : e.pageX) : 0
  const tmpDeltaY = axis && axis !== 'x' ? _mouseClickPosition.mouseY - (e.touches ? e.touches[0].pageY : e.pageY) : 0
  const [deltaX, deltaY] = snapToGrid(grid, tmpDeltaX, tmpDeltaY, props.scaleRatio)
  const _left = restrictToBounds(_mouseClickPosition.left - deltaX, _bounds.minLeft, _bounds.maxLeft)
  const _top = restrictToBounds(_mouseClickPosition.top - deltaY, _bounds.minTop, _bounds.maxTop)
  if (props.onDrag(_left, _top) === false) {
    return
  }
  const _right = restrictToBounds(_mouseClickPosition.right + deltaX, _bounds.minRight, _bounds.maxRight)
  const _bottom = restrictToBounds(_mouseClickPosition.bottom + deltaY, _bounds.minBottom, _bounds.maxBottom)
  left.value = _left
  top.value = _top
  right.value = _right
  bottom.value = _bottom
  await snapCheck()
  emits('dragging', left.value, top.value)
}
// 检测对齐元素
async function snapCheck() {
  let _width = width.value
  let _height = height.value
  if (props.snap) {
    let activeLeft = left.value
    let activeRight = left.value + _width
    let activeTop = top.value
    let activeBottom = top.value + _height

    // 初始化辅助线数据
    const temArr = new Array(3).fill({ display: false, position: '', origin: '', lineLength: '' })
    const refLine: { [k: string]: any } = { vLine: [], hLine: [] }
    for (let i in refLine) { refLine[i] = JSON.parse(JSON.stringify(temArr)) }
    // 获取当前父节点下所有子节点
    const nodes = wftCustomDraggableRef.value!.parentNode!.childNodes as NodeListOf<ChildNode & { [k: string]: any }>
    let tem: { [k: string]: any } = {
      value: { x: [[], [], []], y: [[], [], []] },
      display: [],
      position: []
    }
    const { groupWidth, groupHeight, groupLeft, groupTop, bln } = await getActiveAll(nodes)
    if (!bln) {
      _width = groupWidth
      _height = groupHeight
      activeLeft = groupLeft
      activeRight = groupLeft + groupWidth
      activeTop = groupTop
      activeBottom = groupTop + groupHeight
    }
    for (let item of nodes) {
      if (item.className !== undefined && !item.className.includes(props.classNameActive) && item.getAttribute('data-is-snap') !== null && item.getAttribute('data-is-snap') !== 'false') {
        const w = item.offsetWidth
        const h = item.offsetHeight
        const [l, t] = formatTransformVal(item.style.transform)
        const r = l + w // 对齐目标right
        const b = t + h // 对齐目标的bottom
        const hc = Math.abs((activeTop + _height / 2) - (t + h / 2)) <= props.snapTolerance // 水平中线
        const vc = Math.abs((activeLeft + _width / 2) - (l + w / 2)) <= props.snapTolerance // 垂直中线
        const ts = Math.abs(t - activeBottom) <= props.snapTolerance // 从上到下
        const TS = Math.abs(b - activeBottom) <= props.snapTolerance // 从上到下
        const bs = Math.abs(t - activeTop) <= props.snapTolerance // 从下到上
        const BS = Math.abs(b - activeTop) <= props.snapTolerance // 从下到上
        const ls = Math.abs(l - activeRight) <= props.snapTolerance // 外左
        const LS = Math.abs(r - activeRight) <= props.snapTolerance // 外左
        const rs = Math.abs(l - activeLeft) <= props.snapTolerance // 外右
        const RS = Math.abs(r - activeLeft) <= props.snapTolerance // 外右
        tem['display'] = [ts, TS, bs, BS, hc, hc, ls, LS, rs, RS, vc, vc]
        tem['position'] = [t, b, t, b, t + h / 2, t + h / 2, l, r, l, r, l + w / 2, l + w / 2]
        // fix：中线自动对齐，元素可能超过父元素边界的问题
        if (ts) {
          if (bln) {
            top.value = Math.max(t - _height, bounds.value.minTop)
            bottom.value = parentHeight.value - top.value - _height
          }
          tem.value.y[0].push(l, r, activeLeft, activeRight)
        }
        if (bs) {
          if (bln) {
            top.value = t
            bottom.value = parentHeight.value - top.value - _height
          }
          tem.value.y[0].push(l, r, activeLeft, activeRight)
        }
        if (TS) {
          if (bln) {
            top.value = Math.max(b - _height, bounds.value.minTop)
            bottom.value = parentHeight.value - top.value - _height
          }
          tem.value.y[1].push(l, r, activeLeft, activeRight)
        }
        if (BS) {
          if (bln) {
            top.value = b
            bottom.value = parentHeight.value - top.value - _height
          }
          tem.value.y[1].push(l, r, activeLeft, activeRight)
        }
        if (ls) {
          if (bln) {
            left.value = Math.max(l - _width, bounds.value.minLeft)
            right.value = parentWidth.value - left.value - _width
          }
          tem.value.x[0].push(t, b, activeTop, activeBottom)
        }
        if (rs) {
          if (bln) {
            left.value = l
            right.value = parentWidth.value - left.value - _width
          }
          tem.value.x[0].push(t, b, activeTop, activeBottom)
        }
        if (LS) {
          if (bln) {
            left.value = Math.max(r - _width, bounds.value.minLeft)
            right.value = parentWidth.value - left.value - _width
          }
          tem.value.x[1].push(t, b, activeTop, activeBottom)
        }
        if (RS) {
          if (bln) {
            left.value = r
            right.value = parentWidth.value - left.value - _width
          }
          tem.value.x[1].push(t, b, activeTop, activeBottom)
        }
        if (hc) {
          if (bln) {
            top.value = Math.max(t + h / 2 - _height / 2, bounds.value.minTop)
            bottom.value = parentHeight.value - top.value - _height
          }
          tem.value.y[2].push(l, r, activeLeft, activeRight)
        }
        if (vc) {
          if (bln) {
            left.value = Math.max(l + w / 2 - _width / 2, bounds.value.minLeft)
            right.value = parentWidth.value - left.value - _width
          }
          tem.value.x[2].push(t, b, activeTop, activeBottom)
        }
        // 辅助线坐标与是否显示(display)对应的数组,易于循环遍历
        const arrTem = [0, 1, 0, 1, 2, 2, 0, 1, 0, 1, 2, 2]
        for(let i = 0; i <= arrTem.length; i++) {
          // 前6为Y辅助线,后6为X辅助线
          const xory = i < 6 ? 'y' : 'x'
          const horv = i < 6 ? 'hLine' : 'vLine'
          if (tem.display[i]) {
            const { origin, length } = calcLineValues(tem.value[xory][arrTem[i]])
            refLine[horv][arrTem[i]].display = tem.display[i]
            refLine[horv][arrTem[i]].position = tem.position[i] + 'px'
            refLine[horv][arrTem[i]].origin = origin
            refLine[horv][arrTem[i]].lineLength = length
          }
        }
      }
    }
    emits('refLineParams', refLine)
  }
}
function calcLineValues(arr: any) {
  const length = Math.max(...arr) - Math.min(...arr) + 'px'
  const origin = Math.min(...arr) + 'px'
  return { length, origin }
}
async function getActiveAll(nodes: any) {
  const activeAll = []
  const XArray = []
  const YArray = []
  let groupWidth = 0
  let groupHeight = 0
  let groupLeft = 0
  let groupTop = 0
  for (let item of nodes) {
    if (item.className !== undefined && item.className.includes(props.classNameActive)) {
      activeAll.push(item)
    }
  }
  const AllLength = activeAll.length
  if (AllLength > 1) {
    for (let i of activeAll) {
      const l = i.offsetLeft
      const r = l + i.offsetWidth
      const t = i.offsetTop
      const b = t + i.offsetHeight
      XArray.push(t, b)
      YArray.push(l, r)
    }
    groupWidth = Math.max(...YArray) - Math.min(...YArray)
    groupHeight = Math.max(...XArray) - Math.min(...XArray)
    groupLeft = Math.min(...YArray)
    groupTop = Math.min(...XArray)
  }
  const bln = AllLength === 1
  return { groupWidth, groupHeight, groupLeft, groupTop, bln }
}
// 从控制柄松开
async function handleUp() {
  handle.value = null
  // 初始化辅助线数据
  const temArr = new Array(3).fill({ display: false, position: '', origin: '', lineLength: '' })
  const refLine: { [k: string]: any } = { vLine: [], hLine: [] }
  for (let i in refLine) { refLine[i] = JSON.parse(JSON.stringify(temArr)) }

  if (resizing.value) {
    resizing.value = false
    conflictCheck()
    emits('refLineParams', refLine)
    emits('resizestop', left.value, top.value, width.value, height.value)
  }
  if (dragging.value) {
    dragging.value = false
    conflictCheck()
    emits('refLineParams', refLine)
    emits('dragstop', left.value, top.value)
  }
  resetBoundsAndMouseState()
  removeEvent(document.documentElement, eventsFor.move, handleResize)
}
// 冲突检测
function conflictCheck() {
  const _top = top.value
  const _left = left.value
  const _width = width.value
  const _height = height.value
  if (props.isConflictCheck) {
    const nodes = wftCustomDraggableRef.value!.parentNode!.childNodes as NodeListOf<ChildNode & { [k: string]: any }> // 获取当前父节点下所有子节点
    for (let item of nodes) {
      if (item.className !== undefined && !item.className.includes(props.classNameActive) && item.getAttribute('data-is-check') !== null && item.getAttribute('data-is-check') !== 'false') {
        const tw = item.offsetWidth
        const th = item.offsetHeight
        // 正则获取left与right
        let [tl, tt] = formatTransformVal(item.style.transform)

        // 左上角与右下角重叠
        const tfAndBr = (_top >= tt && _left >= tl && tt + th > _top && tl + tw > _left) || (_top <= tt && _left < tl && _top + _height > tt && _left + _width > tl)
        // 右上角与左下角重叠
        const brAndTf = (_left <= tl && _top >= tt && _left + _width > tl && _top < tt + th) || (_top < tt && _left > tl && _top + _height > tt && _left < tl + tw)
        // 下边与上边重叠
        const bAndT = (_top <= tt && _left >= tl && _top + _height > tt && _left < tl + tw) || (_top >= tt && _left <= tl && _top < tt + th && _left > tl + tw)
        // 上边与下边重叠（宽度不一样）
        const tAndB = (_top <= tt && _left >= tl && _top + _height > tt && _left < tl + tw) || (_top >= tt && _left <= tl && _top < tt + th && _left > tl + tw)
        // 左边与右边重叠
        const lAndR = (_left >= tl && _top >= tt && _left < tl + tw && _top < tt + th) || (_top > tt && _left <= tl && _left + _width > tl && _top < tt + th)
        // 左边与右边重叠（高度不一样）
        const rAndL = (_top <= tt && _left >= tl && _top + _height > tt && _left < tl + tw) || (_top >= tt && _left <= tl && _top < tt + th && _left + _width > tl)
        // 如果冲突，就将回退到移动前的位置
        if (tfAndBr || brAndTf || bAndT || tAndB || lAndR || rAndL) {
          top.value = mouseClickPosition.value.top
          left.value = mouseClickPosition.value.left
          right.value = mouseClickPosition.value.right
          bottom.value = mouseClickPosition.value.bottom
          width.value = mouseClickPosition.value.w
          height.value = mouseClickPosition.value.h
          emits('resizing', left.value, top.value, width.value, height.value)
        }
      }
    }
  }
}
// 正则获取left与top
function formatTransformVal(string: string) {
  let [left, top]: any[] = string.replace(/[^0-9\-,]/g, '').split(',')
  if (top === undefined) top = 0
  return [+left, +top]
}
// 检查父元素大小
function checkParentSize () {
  if (props.parent) {
    const [newParentWidth, newParentHeight] = getParentSize()
    // 修复父元素改变大小后，组件resizing时活动异常
    right.value = newParentWidth! - width.value - left.value
    bottom.value = newParentHeight! - height.value - top.value
    parentWidth.value = newParentWidth as number
    parentHeight.value = newParentHeight as number
  }
}
// 取消
function deselect (e: any) {
  const target = e.target || e.srcElement
  const regex = new RegExp(props.className + '-([trmbl]{2})', '')
  if (!wftCustomDraggableRef.value!.contains(target) && !regex.test(target.className)) {
    // Edit By WFT START
    if(props.panelContainer) {
      if(!document.querySelector(props.panelContainer)?.contains(target)) {
        return
      }
    }
    if(props.excludeContainer) {
      if(document.querySelector(props.excludeContainer)?.contains(target)) {
        return
      }
    }
    // Edit By WFT END
    if (enabled.value && !props.preventDeactivation) {
      enabled.value = false

      emits('deactivated')
      emits('update:active', false)
    }
    removeEvent(document.documentElement, eventsFor.move, handleResize)
  }
  resetBoundsAndMouseState()
}

// 控制柄移动
function handleResize(e: any) {
  let _left = left.value
  let _top = top.value
  let _right = right.value
  let _bottom = bottom.value
  const _mouseClickPosition = mouseClickPosition.value
  const _aspectFactor = aspectFactor.value
  const tmpDeltaX = _mouseClickPosition.mouseX - (e.touches ? e.touches[0].pageX : e.pageX)
  const tmpDeltaY = _mouseClickPosition.mouseY - (e.touches ? e.touches[0].pageY : e.pageY)
  if (!widthTouched.value && tmpDeltaX) {
    widthTouched.value = true
  }
  if (!heightTouched.value && tmpDeltaY) {
    heightTouched.value = true
  }
  const [deltaX, deltaY] = snapToGrid(props.grid, tmpDeltaX, tmpDeltaY, props.scaleRatio)
  if(handle.value!.includes('b')) {
    _bottom = restrictToBounds(
      _mouseClickPosition.bottom + deltaY,
      bounds.value.minBottom,
      bounds.value.maxBottom
    )
    if (props.lockAspectRatio && resizingOnY.value) {
      _right = right.value - (bottom.value - _bottom) * _aspectFactor
    }
  } else if (handle.value!.includes('t')) {
    _top = restrictToBounds(
      _mouseClickPosition.top - deltaY,
      bounds.value.minTop,
      bounds.value.maxTop
    )
    if(props.lockAspectRatio && resizingOnY.value) {
      _left = left.value - (top.value - _top) * _aspectFactor
    }
  }
  if(handle.value!.includes('r')) {
    _right = restrictToBounds(
      _mouseClickPosition.right + deltaX,
      bounds.value.minRight,
      bounds.value.maxRight
    )
    if (props.lockAspectRatio && resizingOnX.value) {
      _bottom = bottom.value - (right.value - _right) / _aspectFactor
    }
  } else if (handle.value!.includes('l')) {
    _left = restrictToBounds(
      _mouseClickPosition.left - deltaX,
      bounds.value.minLeft,
      bounds.value.maxLeft
    )
    if (props.lockAspectRatio && resizingOnX.value) {
      _top = top.value - (left.value - _left) / _aspectFactor
    }
  }
  const _width = computeWidth(parentWidth.value, _left, _right)
  const _height = computeHeight(parentHeight.value, _top, _bottom)
  if (props.onResize(handle.value, _left, _top, _width, _height) === false) {
    return
  }
  left.value = _left
  top.value = _top
  right.value = _right
  bottom.value = _bottom
  width.value = _width
  height.value = _height
  emits('resizing', left.value, top.value, width.value, height.value)
}

// 获取父元素大小
function getParentSize () {
  if (props.parent === true) {
    const style = window.getComputedStyle(wftCustomDraggableRef.value!.parentNode as Element, null)
    return [
      parseInt(style.getPropertyValue('width'), 10),
      parseInt(style.getPropertyValue('height'), 10)
    ]
  }
  if (typeof props.parent === 'string') {
    const parentNode = document.querySelector(props.parent)
    if (!(parentNode instanceof HTMLElement)) {
      throw new Error(`The selector ${props.parent} does not match any element`)
    }
    return [parentNode.offsetWidth, parentNode.offsetHeight]
  }
  return [null, null]
}

// 重置边界和鼠标状态
function resetBoundsAndMouseState () {
  mouseClickPosition.value = { mouseX: 0, mouseY: 0, x: 0, y: 0, w: 0, h: 0 }
  bounds.value = {
    minLeft: null,
    maxLeft: null,
    minRight: null,
    maxRight: null,
    minTop: null,
    maxTop: null,
    minBottom: null,
    maxBottom: null
  }
}

// 计算移动范围
function calcDragLimits () {
  return {
    minLeft: left.value % props.grid[0],
    maxLeft: Math.floor((parentWidth.value - width.value - left.value) / props.grid[0]) * props.grid[0] + left.value,
    minRight: right.value % props.grid[0],
    maxRight: Math.floor((parentWidth.value - width.value - right.value) / props.grid[0]) * props.grid[0] + right.value,
    minTop: top.value % props.grid[1],
    maxTop: Math.floor((parentHeight.value - height.value - top.value) / props.grid[1]) * props.grid[1] + top.value,
    minBottom: bottom.value % props.grid[1],
    maxBottom: Math.floor((parentHeight.value - height.value - bottom.value) / props.grid[1]) * props.grid[1] + bottom.value
  }
}

function moveHorizontally(val: number) {
  const [deltaX, _] = snapToGrid(props.grid, val, top.value)
  const left = restrictToBounds(deltaX, bounds.value.minLeft, bounds.value.maxLeft)
  left.value = left
  right.value = parentWidth.value - width.value - left
}

function moveVertically(val: number) {
  const [_, deltaY] = snapToGrid(props.grid, left.value, val)
  const top = restrictToBounds(deltaY, bounds.value.minTop, bounds.value.maxTop)
  top.value = top
  bottom.value = parentHeight.value - height.value - top
}

// 计算调整大小范围
function calcResizeLimits () {
  let _minW = minW.value as number
  let _minH = minH.value as number
  let _maxW = maxW.value as number
  let _maxH = maxH.value as number
  const _aspectFactor = aspectFactor.value as number
  const [gridX, gridY] = props.grid
  const _width = width.value
  const _height = height.value
  const _left = left.value
  const _top = top.value
  const _right = right.value
  const _bottom = bottom.value
  if (props.lockAspectRatio) {
    if (_minW / _minH > _aspectFactor) {
      _minH = _minW / _aspectFactor
    } else {
      _minW = _aspectFactor * _minH
    }

    if (_maxW && _maxH) {
      _maxW = Math.min(_maxW, _aspectFactor * _maxH)
      _maxH = Math.min(_maxH, _maxW / _aspectFactor)
    } else if (_maxW) {
      _maxH = _maxW / _aspectFactor
    } else if (_maxH) {
      _maxW = _aspectFactor * _maxH
    }
  }
  _maxW = _maxW - (_maxW % gridX)
  _maxH = _maxH - (_maxH % gridY)
  const limits: { [k: string]: any } = {
    minLeft: null,
    maxLeft: null,
    minTop: null,
    maxTop: null,
    minRight: null,
    maxRight: null,
    minBottom: null,
    maxBottom: null
  }
  if (props.parent) {
    limits.minLeft = _left % gridX
    limits.maxLeft = _left + Math.floor((_width - _minW) / gridX) * gridX
    limits.minTop = _top % gridY
    limits.maxTop = _top + Math.floor((_height - _minH) / gridY) * gridY
    limits.minRight = _right % gridX
    limits.maxRight = _right + Math.floor((_width - _minW) / gridX) * gridX
    limits.minBottom = _bottom % gridY
    limits.maxBottom = _bottom + Math.floor((_height - _minH) / gridY) * gridY
    if (_maxW) {
      limits.minLeft = Math.max(limits.minLeft, parentWidth.value - _right - _maxW)
      limits.minRight = Math.max(limits.minRight, parentWidth.value - _left - _maxW)
    }
    if (_maxH) {
      limits.minTop = Math.max(limits.minTop, parentHeight.value - _bottom - _maxH)
      limits.minBottom = Math.max(limits.minBottom, parentHeight.value - _top - _maxH)
    }
    if (props.lockAspectRatio) {
      limits.minLeft = Math.max(limits.minLeft, _left - _top * _aspectFactor)
      limits.minTop = Math.max(limits.minTop, _top - _left / _aspectFactor)
      limits.minRight = Math.max(limits.minRight, _right - _bottom * _aspectFactor)
      limits.minBottom = Math.max(limits.minBottom, _bottom - _right / _aspectFactor)
    }
  } else {
    limits.minLeft = null
    limits.maxLeft = _left + Math.floor((_width - _minW) / gridX) * gridX
    limits.minTop = null
    limits.maxTop = _top + Math.floor((_height - _minH) / gridY) * gridY
    limits.minRight = null
    limits.maxRight = _right + Math.floor((_width - _minW) / gridX) * gridX
    limits.minBottom = null
    limits.maxBottom = _bottom + Math.floor((_height - _minH) / gridY) * gridY
    if (_maxW) {
      limits.minLeft = -(_right + _maxW)
      limits.minRight = -(_left + _maxW)
    }
    if (_maxH) {
      limits.minTop = -(_bottom + _maxH)
      limits.minBottom = -(_top + _maxH)
    }
    if (props.lockAspectRatio && (_maxW && _maxH)) {
      limits.minLeft = Math.min(limits.minLeft, -(_right + _maxW))
      limits.minTop = Math.min(limits.minTop, -(_maxH + _bottom))
      limits.minRight = Math.min(limits.minRight, -left - _maxW)
      limits.minBottom = Math.min(limits.minBottom, -top - _maxH)
    }
  }
  return limits
}

function changeWidth(val: number) {
  const [newWidth, _] = snapToGrid(props.grid, val, 0)
  let right = restrictToBounds(
    (parentWidth.value - newWidth - left.value),
    bounds.value.minRight,
    bounds.value.maxRight
  )
  let _bottom = bottom.value
  if (props.lockAspectRatio) {
    _bottom = bottom.value - (right.value - right) / aspectFactor.value
  }
  const width = computeWidth(parentWidth.value, left.value, right)
  const height = computeHeight(parentHeight.value, top.value, _bottom)
  right.value = right
  bottom.value = _bottom
  width.value = width
  height.value = height
}

function changeHeight(val: number) {
  const [_, newHeight] = snapToGrid(props.grid, 0, val)
  let bottom = restrictToBounds(
    (parentHeight.value - newHeight - top.value),
    bounds.value.minBottom,
    bounds.value.maxBottom
  )
  let _right = right.value
  if (props.lockAspectRatio) {
    _right = right.value - (bottom.value - bottom) * aspectFactor.value
  }
  const width = computeWidth(parentWidth.value, left.value, _right)
  const height = computeHeight(parentHeight.value, top.value, bottom)
  right.value = _right
  bottom.value = bottom
  width.value = width
  height.value = height
}

// 设置属性
function settingAttribute() {
  // 设置冲突检测
  wftCustomDraggableRef.value!.setAttribute('data-is-check', `${props.isConflictCheck}`)
  // 设置对齐元素
  wftCustomDraggableRef.value!.setAttribute('data-is-snap', `${props.snap}`)
}

</script>
<style scoped>

</style>